package org.fuys.coder.domain.substance.service.repo.impl;

import lombok.extern.slf4j.Slf4j;
import org.fuys.coder.infrastructure.model.dto.SubstanceAuthorDTO;
import org.fuys.coder.infrastructure.model.dto.SubstanceDTO;
import org.fuys.coder.domain.config.service.impl.CoderRecommendConfig;
import org.fuys.coder.common.constants.RedisConstants;
import org.fuys.coder.common.constants.ResultMessageConstants;
import org.fuys.coder.common.exception.BusinessException;
import org.fuys.coder.common.exception.handler.NeedCallBackException;
import org.fuys.coder.common.holder.CategoryVOHolder;
import org.fuys.coder.domain.audit.model.event.AsyncAuditEvent;
import org.fuys.coder.domain.audit.model.req.AuditTask;
import org.fuys.coder.domain.audit.model.res.AuditResult;
import org.fuys.coder.domain.audit.model.vo.AuditResultTypeVO;
import org.fuys.coder.domain.audit.model.vo.AuditTaskIdTypeVO;
import org.fuys.coder.domain.audit.model.vo.AuditTaskTypeVO;
import org.fuys.coder.domain.audit.service.Audit;
import org.fuys.coder.domain.audit.service.publisher.AsyncAuditEventPublisher;
import org.fuys.coder.domain.auth.model.vo.token.TokenTypeVO;
import org.fuys.coder.domain.auth.service.vertify.token.ITrustTokenService;
import org.fuys.coder.domain.category.model.vo.CategoryVO;
import org.fuys.coder.common.entity.PageDTO;
import org.fuys.coder.domain.feed.service.IFeedPushService;
import org.fuys.coder.domain.file.service.AliYunOSSService;
import org.fuys.coder.domain.file.service.IFileService;
import org.fuys.coder.domain.mailbox.model.req.MailReq;
import org.fuys.coder.domain.mailbox.model.vo.MailVO;
import org.fuys.coder.domain.substance.model.event.SubstanceReviewEvent;
import org.fuys.coder.domain.substance.model.req.admin.SubstancePublishReq;
import org.fuys.coder.domain.substance.model.vo.SubstanceDetailVO;
import org.fuys.coder.domain.substance.model.vo.SubstanceFileTypeVO;
import org.fuys.coder.domain.substance.model.vo.SubstanceSupplyVO;
import org.fuys.coder.domain.substance.model.vo.SubstanceVO;
import org.fuys.coder.domain.substance.repository.ISubstanceRepository;
import org.fuys.coder.domain.substance.service.repo.ISubstanceService;
import org.fuys.coder.domain.user.model.vo.repo.AuthorVO;
import org.jetbrains.annotations.Nullable;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @projectName: fuys-low-coder
 * @package: org.fuys.coder.domain.substance.service.repo.impl
 * @className: SubstanceService
 * @author: WangJing
 * @description:
 * @date: 2024/6/26 16:17
 * @version: 1.0
 */
@Service
@Slf4j
public class SubstanceServiceImpl implements ISubstanceService {

    @Resource
    private ISubstanceRepository substanceRepository;

    @Resource
    private AsyncAuditEventPublisher asyncAuditEventPublisher;

    @Resource
    private IFileService fileService;

    @Resource
    private Audit audit;

    @Resource
    private CoderRecommendConfig recommendConfig;

    @Resource
    private IFeedPushService feedPushService;

    @Resource
    private AliYunOSSService aliYunOSSService;

    @Resource
    private ITrustTokenService tokenService;

    @Override
    public List<SubstanceVO> getSubstanceByCategories(List<Integer> categoryList,int curPage,int substanceSize) {
        final List<SubstanceDTO> substances =
                substanceRepository.getSubstanceByCategories(categoryList,curPage,substanceSize);
        return wrapDTOListToVOList(substances);
    }

    private void baseWrapSubstanceDTOToVO(SubstanceDTO substance, SubstanceVO substanceVO) {
        substanceVO.setCreateTime(substance.getCreateTime());
        substanceVO.setDescription(substance.getDescription());
        substanceVO.setFavoriteCount(substance.getFavoriteCount());
        substanceVO.setHateCount(substance.getHateCount());
        substanceVO.setId(substance.getId());
        substanceVO.setThumbCount(substance.getThumbCount());
        substanceVO.setTitle(substance.getTitle());
        substanceVO.setCategoryId(substance.getCategoryId());
        substanceVO.setCategoryName(substance.getCategoryName());
        substanceVO.setCoverId(substance.getCoverId());
        substanceVO.setCoverUrl(aliYunOSSService.buildUrl(substance.getCoverId()));
        final AuthorVO authorVO = new AuthorVO();
        authorVO.setAvatar(substance.getAvatar());
        authorVO.setIntroduce(substance.getNickName());
        authorVO.setUserName(substance.getNickName());
        authorVO.setUserId(substance.getUserId());
        authorVO.setFollowCount(substance.getFollowerCount());
        authorVO.setAvatarUrl(aliYunOSSService.buildUrl(substance.getAvatar()));
        substanceVO.setAuthorVO(authorVO);
    }

//    @Override
//    public Pair<String[], Long> getPublishToken(Integer userId) {
////        final String token = UUID.randomUUID().toString();
//        final long publishTokenExpire = coderSecurityConfig.getPublishTokenExpire();
////        final String redisKey = RedisConstants.REDIS_FIELD_USER + RedisConstants.REDIS_USE_PUBLISH_TOKEN +
////                RedisConstants.REDIS_SPLIT + userId;
////        final Object o = redisUtil.commonGet(redisKey);
////        if(!ObjectUtils.isEmpty(o)){
////            final String publishToken=o.toString();
////            return new Pair<>(publishToken,publishTokenExpire);
////        }
////        redisUtil.commonSet(redisKey,token, publishTokenExpire,TimeUnit.HOURS);
//        final String token = tokenService.operationToken(userId, null, TokenTypeVO.SUBSTANCE_PUBLISH)[0];
//        return new Pair<>(token,publishTokenExpire);
//    }

    @Override
    @Transactional(rollbackFor = NeedCallBackException.class)
    public void publishSubstance(SubstancePublishReq req) {
        final String redisKey = RedisConstants.REDIS_FIELD_USER + RedisConstants.REDIS_USE_PUBLISH_TOKEN +
                RedisConstants.REDIS_SPLIT + req.getAuthorId();
//        final String token = redisUtil.commonGet(redisKey).toString();
        final boolean flag = tokenService.checkToken(req.getAuthorId(), null, TokenTypeVO.SUBSTANCE_PUBLISH, req.getToken());
//        if(ObjectUtils.isEmpty(token)){
//            throw new BusinessException(ResultMessageConstants.UNTRUSTED_USERS);
//        }
        if(!flag){
            throw new BusinessException(ResultMessageConstants.ILLEGAL_OPERATION);
        }
        //首先进行插入数据库操作 插入成功与否 都删除此次发布内容的合法token
        Long substanceId=substanceRepository.addSubstance(req);
        //构建审核任务 此条记录最后是否会被删除取决于审核的结果
        AuditTask auditTask=new AuditTask();
        List<String> tasks=new ArrayList<>();
        tasks.add(req.getTitle());
        tasks.add(req.getIntroduce());
        tasks.add("|");
        tasks.addAll(this.getFileKey(new ArrayList<>(req.getFileMap().values())));
        auditTask.setType(AuditTaskTypeVO.MULTIPLE.getIndex());
        auditTask.setUserId(req.getAuthorId());
        auditTask.setIdType(AuditTaskIdTypeVO.SUBSTANCE.getIndex());
        auditTask.setOtherId(substanceId);
        auditTask.setNeedResultHandle(true);
        auditTask.setTasks(tasks.toArray(new String[0]));
        auditTask.setCallback((unused -> {
            //todo 采用推拉结合的模式吧 现在先修改活动的收件箱发送
            substanceRepository.setSubstanceStatusById(substanceId, AuditResultTypeVO.PASS);
            if(req.getFollowers()<=recommendConfig.getPushCount()){
                try {
                    MailReq mailReq=new MailReq();
                    MailVO mailVO=new MailVO();
                    mailVO.setFromId(req.getAuthorId());
                    //收件箱推送的时候 我们只需要标题 封面 作者名称就行 当然 隐含的是作品id
                    SubstanceVO substanceVO=new SubstanceVO();
                    substanceVO.setTitle(req.getTitle());
                    substanceVO.setCoverId(req.getCover());
                    substanceVO.setId(substanceId);
                    mailVO.setData(substanceVO);
                    mailReq.setMailVO(mailVO);
                    feedPushService.pushToFollowers(mailReq);
                } catch (Exception e) {
                    log.error("推送过程中出现了错误");
                    e.printStackTrace();
                }
            }
            return null;
        }));
        audit.doAudit(auditTask);
    }

    //根据文件ID查询文件ID
    private List<String> getFileKey(List<Long> fileIds){
        return fileService.getFileKeys(fileIds);
    }


    @Override
    public SubstanceSupplyVO getSubstanceSupply(Long substanceId) {
        final SubstanceSupplyVO substanceSupply = this.substanceRepository.getSubstanceSupply(substanceId);
        if(ObjectUtils.isEmpty(substanceSupply)){
            return null;
        }else{
            Map<String,String> urlMap=new HashMap<>();
            substanceSupply.getFileMap().forEach((key,value)->{
                final String url = aliYunOSSService.buildUrl(value);
                urlMap.put(SubstanceFileTypeVO.desc(key),url);
            });
            substanceSupply.setFileUrlMap(urlMap);
            return substanceSupply;
        }
    }

    @Override
    public SubstanceDetailVO getSubstanceDetail(Long substanceId) {
        final SubstanceSupplyVO substanceSupply = this.getSubstanceSupply(substanceId);
        if(ObjectUtils.isEmpty(substanceSupply)){
            return null;
        }
        SubstanceDetailVO detailVO=new SubstanceDetailVO();
        detailVO.setSupplyVO(substanceSupply);
        SubstanceAuthorDTO substanceAuthorDTO = substanceRepository.getSubstanceAndAuthorIntro(substanceId);
        AuthorVO authorVO=new AuthorVO();
        authorVO.setUserId(substanceAuthorDTO.getUserId());
        authorVO.setFollowCount(substanceAuthorDTO.getFollowerCount());
        authorVO.setIntroduce(substanceAuthorDTO.getIntroduce());
        authorVO.setUserId(substanceAuthorDTO.getUserId());
        authorVO.setUserName(substanceAuthorDTO.getNickName());
        authorVO.setAvatar(substanceAuthorDTO.getAvatar());
        authorVO.setAvatarUrl(aliYunOSSService.buildUrl(substanceAuthorDTO.getAvatar()));
        detailVO.setAuthorVO(authorVO);
        detailVO.setDescription(substanceAuthorDTO.getDescription());
        detailVO.setTitle(substanceAuthorDTO.getTitle());
        detailVO.setThumbCount(substanceAuthorDTO.getThumbCount());
        detailVO.setFavCount(substanceAuthorDTO.getFavoriteCount());
        return detailVO;
    }


    @Override
    public List<SubstanceVO> getSubstanceAfterTime(Date dateBeforeCur, Integer hotSelectNum) {
        List<SubstanceDTO> result=substanceRepository.getSubstanceAfterTime(dateBeforeCur,hotSelectNum);
        return result.stream().map(substance -> {
            SubstanceVO substanceVO = new SubstanceVO();
            baseWrapSubstanceDTOToVO(substance, substanceVO);
            return substanceVO;
        }).collect(Collectors.toList());
    }

    @Override
    public List<SubstanceVO> getAuthorSubstancesByOpenFlag(Integer userId, PageDTO pageDTO, boolean showNotOpen) {
        final List<SubstanceVO> resultList = substanceRepository.getAuthorSubstancesByOpenFlag(userId,pageDTO,showNotOpen);
        if(ObjectUtils.isEmpty(resultList)){
            return null;
        }
        resultList.forEach(item->{item.setCoverUrl(aliYunOSSService.buildUrl(item.getCoverId()));});
        return resultList;
    }

    @Component
    public class SubstanceReviewListener implements ApplicationListener<SubstanceReviewEvent>{

        @Override
        public void onApplicationEvent(SubstanceReviewEvent event) {
            //这里还是需要等内容模块处理好 目前是想要将内容和其包含的file 如html文本 和图片 再存放到其他表中 这样的话
            //这样的话 就需要获取文件的fileKey进行封装了 然后交给审核进行操作
            final Long substanceId = event.getSubstanceId();
            //如果文件类型是null 说明是标题等文本
            AuditTask task=new AuditTask();
            task.setIdType(AuditTaskIdTypeVO.SUBSTANCE.getIndex());
            task.setOtherId(substanceId);
            task.setUserId(event.getAuthorId());
            AuditResult result=new AuditResult();
            result.setFinish(true);
            result.setType(AuditResultTypeVO.REVIEW.getIndex());
            boolean flag=false;
            if(ObjectUtils.isEmpty(event.getFileType())){
                flag=true;
                result.setMsg("标题 简介被用户举报次数过多");
            }else if(event.getFileType().equals(SubstanceFileTypeVO.IMAGE.getIdx())){
                //判断是否是上传的图片存在问题
                flag=true;
                result.setMsg("内容的图片被用户举报次数过多");
            }else if(event.getFileType().equals(0)){
                //否则是全部
                flag=true;
                result.setMsg("此内容被用户举报次数过多");
            }
            //如果是预期的行为 则进行操作
            if (flag) {
                final AsyncAuditEvent reviewEvent = new AsyncAuditEvent(this);
                reviewEvent.setAuditResult(result);
                reviewEvent.setAuditTask(task);
                asyncAuditEventPublisher.publishAuditEvent(reviewEvent);
            }
            //否则无视这次举报
        }
    }

    @Override
    public List<SubstanceVO> getSubstanceByName(PageDTO pageDTO, String keywords) {
        //获取内容
        final List<SubstanceDTO> substanceByName = substanceRepository.getSubstanceByName(pageDTO, keywords);
        //判空 如空不进行包装操作
        return wrapDTOListToVOList(substanceByName);
    }

    @Nullable
    private List<SubstanceVO> wrapDTOListToVOList(List<SubstanceDTO> substanceByName) {
        if(ObjectUtils.isEmpty(substanceByName)){
            return null;
        }
        List<SubstanceVO> substanceVOs=new ArrayList<>();
        substanceByName.forEach(item->{
            SubstanceVO substanceVO = new SubstanceVO();
            final CategoryVO categoryVO = CategoryVOHolder.getById(item.getCategoryId());
            substanceVO.setCategoryId(categoryVO.getId());
            substanceVO.setCategoryName(categoryVO.getName());
            baseWrapSubstanceDTOToVO(item,substanceVO);
            substanceVOs.add(substanceVO);
        });
        return substanceVOs;
    }

    @Override
    public SubstanceVO getSubstanceById(Long id) {
        SubstanceDTO dto = substanceRepository.getSubstanceById(id);
        SubstanceVO vo=new SubstanceVO();
        baseWrapSubstanceDTOToVO(dto,vo);
        return vo;
    }

    @Override
    public void modifySubstanceStatus(Long id, Integer status, Integer type) {
        substanceRepository.modifySubstanceStatusById(id,status,type);
    }

    @Override
    public Set<SubstanceVO> getCacheSubstances(String key, Integer count) {
        return substanceRepository.getCacheSubstance(key, count);
    }

    @Override
    public List<SubstanceVO> getSubstanceList(List<Long> id) {
        final List<SubstanceDTO> substanceDTOS = substanceRepository.getSubstances(id);
        if(ObjectUtils.isEmpty(substanceDTOS)){
            return null;
        }
        return wrapDTOListToVOList(substanceDTOS);
    }
}
